【优化】IF | 您所在的位置:网站首页 › java ifelse优化 › 【优化】IF |
1. 前言
代码中,如果 if-else 语句比较多,阅读起来比较困难,维护性较差,很容易出bug。接下来,此文将介绍优化 if-else 代码的七种方案: 如果 if-else 代码块包含 return 语句,可以考虑通过提前 return,把多余 else 干掉,使代码更加优雅 优化前: if (condition) { // TODO } else { return; }优化后: if (!condition) { return; } // TODO 2.2 使用条件三目运算符使用条件三目运算符可以简化某些 if-else,使代码更加简洁,更具有可读性 优化前: int status = 0; if (condition) { status = 1; } else { status = 2; }优化后: int satus = condition ? 1 : 2 2.3 使用枚举在某些时候,使用枚举也可以优化 if-else 逻辑分支(也可以看做一种表驱动方法) 优化前: String orderStatusDes = ""; if (orderStatus == 0) { orderStatusDes = "订单未支付"; } else if (orderStatus == 1) { orderStatusDes = "订单已支付"; } else if (orderStatus == 2) { orderStatusDes = "已发货"; }优化后: 定义一个枚举类: public enum OrderStatusEnum { NOT_EXIST(-1, "不存在") , NOT_PAID(0, "订单未支付") , PAID(1, "订单已支付") , SENDED(2, "已发货"); private Integer code; private String message; OrderStatusEnum(Integer code, String message) { this.code = code; this.message = message; } // 枚举列类中定义一个自定义方法,但如果要想能够被外面访问,需要定义成 static 类型 public static OrderStatusEnum getOrderStatusEnum(Integer status) { for (OrderStatusEnum statusEnum : OrderStatusEnum.values()) { if (status.equals(statusEnum.getMessage())) { return statusEnum; } } return NOT_EXIST; } }有了枚举之后,以上 if-else 逻辑分支,可以优化为: String orderStatusDes = OrderStatusEnum.getOrderStatusEnum(orderStatus).getMessage();如果一个工程中,定义的枚举类较多,可以实现一个共同的接口 2.4 合并条件表达式如果有一系列条件返回一样的结果,可以将它们合并为一个条件表达式,让逻辑更加清晰 优化前: if (age return 100; } // TODO优化后: if (age return 0.0; } if (rate > 0 && duration > 0) { return (income / duration) * rate; } return 0.0;优化后: if (captial return 0.0; } return (income / duration) * rate; 2.6 使用 Optional有时候 if-else 比较多,是因为非空判断导致的,这时候你可以使用 java8 的 Optional 类进行优化 优化前: if (user != null) { if (user.getCity() != null) { if (user.getCity().getCode() != null) { String value = user.getCity().getCode().getValue(); } } }优化后: Optional.ofNullable(user).map(User::getCity).map(City::getCode).map(Code::getValue).orElse("init"); 2.7 策略模式 + 工厂方法消除 if else根据字段名(年龄、姓名、出生地)不同,将对象集合进行排序(可以升序、降序) 优化前: // 属性值 String props = "name"; // 升序、降序 String order = "ascending"; List users = new ArrayList(); // 将 users 进行赋值 if ("age".equals(props )) { if ("ascending".equals(order)) { // 升序排序 } else { // 降序排序 } } else if ("name".equals(props)) { if ("ascending".equals(order)) { // 升序排序 } else { // 降序排序 } } else if ("birthPlace".equals(props)) { if ("ascending".equals(order)) { // 升序排序 } else { // 降序排序 } }优化后: 把每个条件逻辑代码块,抽象成一个公共的接口: public interface PropService { // 升序 void sortAscend(List users); // 降序 void sortDescend(List users); }根据每个逻辑条件,定义相对应的策略实现: public class NamePropServiceImpl implements PropService { @Override public void sortAscend(List users) { Collections.sort(users, Comparator.comparing(User::getName)); } @Override public void sortDescend(List users) { Collections.sort(users, Comparator.comparing(User::getName).reversed()); } } public class AgePropServiceImpl implements PropService { @Override public void sortAscend(List users) { Collections.sort(users, Comparator.comparing(User::getAge)); } @Override public void sortDescend(List users) { Collections.sort(users, Comparator.comparing(User::getAge).reversed()); } } public class BirthPlacePropServiceImpl implements PropService { @Override public void sortAscend(List users) { Collections.sort(users, Comparator.comparing(User::getBirthPlace)); } @Override public void sortDescend(List users) { Collections.sort(users, Comparator.comparing(User::getBirthPlace).reversed()); } }再定义策略工厂类,用来管理这些属性实现策略类,如下: public class PropServiceFactory { private static final Map propServiceMap = new ConcurrentHashMap(); static { propServiceMap.put("name", new NamePropServiceImpl()); propServiceMap.put("age", new AgePropServiceImpl()); propServiceMap.put("birthPlace", new BirthPlacePropServiceImpl()); } public static PropService getPropService(String props) { return propServiceMap.get(props); } }使用了策略 + 工厂模式之后,代码变得简洁多了,如下: public class Test { public static void main(String[] args) { String props = "name"; List user = new ArrayList(); PropService propService = PropServiceFactory.getPropService(props); // 升序 propService.sortAscend(user); // 降序 propService.sortDescend(user); } }在工厂类中,我们需要自己手动将属性策略类添加到一个 Map 中去,那么,当我们需要添加一个新的属性名排序时,就需要修改工厂类的源代码了,所以,这里可以进一步改造!! 2.7.1 改造1:使用 Spring在 Spring 初始化时,需要将策略模式类都注册到工厂类中,将策略类进行改造,使用 Spring 的 InitializingBean 接口,在 bean 初始化后,执行 afterPropertiesSet() 方法进行注册。代码如下: public class NamePropServiceImpl implements PropService, InitializingBean { @Override public void sortAscend(List users) { Collections.sort(users, Comparator.comparing(User::getName)); } @Override public void sortDescend(List users) { Collections.sort(users, Comparator.comparing(User::getName).reversed()); } @Override public void afterPropertiesSet(){ PropServiceFactory.register("name", this); } }其它与之相似,这里就省略了。 工厂类稍作改变: public class PropServiceFactory { private static final Map propServiceMap = new ConcurrentHashMap(); public static void register(String props, PropService propService){ Assert.notNull(props, "Props can't be null"); propServiceMap.put(props, PropService); } public static PropService getPropService(String props) { return propServiceMap.get(props); } } 2.7.2 改造2:使用 枚举 + 反射属性策略实现类都不变 添加一个枚举: public enum PropServiceEnum { NAME_PROP("com.zzc.service.NamePropServiceImpl", "名称") , AGE_PROP("com.zzc.service.AgePropServiceImpl", "年龄") , BIRTH_PLACE_PROP("com.zzc.service.BirthPlacePropServiceImpl", "出生地") ; // 类的全限定名 private String propValue; private String propName; PropServiceEnum(String propValue, String propName) { this.propValue = propValue; this.propName = propName; } public String getPropValue() { return propValue; } public String getPropName() { return propName; } }修改工厂类: public class PropServiceFactory { public static PropService getPropService(String props) { String className = PropServiceEnum.valueOf(props).getClassName(); return (PropService) Class.forName(className).newInstance(); } }=========================================================================================== 2021-12-09 更: 2.8 Spring + 策略模式 + 自定义注解优化前: if ("文本" == msgType) { // TODO } else if ("图片" == msgType) { // TODO } else if ("视频" == msgType) { // TODO } else { // TODO }根据消息的不同类型有不同的处理策略,如果都放在这种 if else 代码块中,代码很难维护,也很丑。所以,这里就用了策略模式来处理这种情况。 策略模式:就是定义一个接口,然后有多个实现类,每种实现类封装了一种行为。然后根据条件的不同选择不同的实现类。 优化后: 定义一个消息对象: @Data @NoArgsConstructor @AllArgsConstructor public class MsgVo { private Integer type; private String content; }定义一个消息类型的枚举类: public enum MsgTypeEnum { TEXT(1, "文本") , IMAGE(2, "图片") , VIDEO(3, "视频") ; private Integer type; private String msg; MsgTypeEnum(Integer type, String msg) { this.type = type; this.msg = msg; } }定义一个消息处理接口: public interface MsgService { void handler(MsgVo msgVo); }处理文本消息实现类: @Service public class TextMsgServiceImpl implements MsgService { @Override public void handler(MsgVo msgVo) { System.out.println("处理文本消息:" + msgVo.getContent()); } }处理图片消息实现类: @Service public class ImageMsgServiceImpl implements MsgService { @Override public void handler(MsgVo msgVo) { System.out.println("处理图片消息:" + msgVo.getContent()); } }我们也可以使用一个 Map 来维护消息类型–消息处理对象关系。这样直接根据消息类型就能拿到消息处理对象,调用消息处理对象的方法即可。 但是我们不想手动维护这个 Map 对象,因为每次增加新的消息处理类,Map 的初始化过程就得修改。 这里使用了 自定义注解 + ApplicationListener 来保存这种映射关系: 自定义注解 MsgTypeHandler: @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MsgTypeHandler { MsgTypeEnum value(); }然后,给每一个类型添加上这个注解类型: @Service @MsgTypeHandler(value = MsgTypeEnum.IMAGE) public class ImageMsgServiceImpl implements MsgService { // ... } @Service @MsgTypeHandler(value = MsgTypeEnum.TEXT) public class TextMsgServiceImpl implements MsgService { // ... }用一个 context 对象保存了消息类型->消息处理对象的映射关系: @Component public class MsgServiceContext { private final Map msgServiceMap = new HashMap(); public MsgService getMsgService(Integer type) { return msgServiceMap.get(type); } public void putMsgService(Integer type, MsgService msgService) { msgServiceMap.put(type, msgService); } }在 Spring 的启动过程中,通过解析注解,将消息类型->消息处理对象的映射关系保存到MsgServiceContext 对象中: @Component public class MsgServiceListener implements ApplicationListener { @Override public void onApplicationEvent(ContextRefreshedEvent event) { Map beans = event.getApplicationContext().getBeansWithAnnotation(MsgTypeHandler.class); MsgServiceContext msgServiceContext = event.getApplicationContext().getBean(MsgServiceContext.class); beans.forEach((name, bean) -> { MsgTypeHandler typeHandler = bean.getClass().getAnnotation(MsgTypeHandler.class); msgServiceContext.putMsgService(typeHandler.value().getType(), (MsgService) bean); }); } }在单元测试中进行测试: @RunWith(SpringRunner.class) @SpringBootTest public class MsgServiceContextTest { @Autowired private MsgServiceContext msgServiceContext; @Test public void contextLoads() { MsgVo msgVo = new MsgVo(MsgTypeEnum.TEXT.getType(), "消息内容"); MsgService msgService = msgServiceContext.getMsgService(msgVo.getType()); msgService.handler(msgVo); } }=========================================================================================== 2022-02-11 更: 2.9 使用函数式接口进行优化基于 2.8 案例。 在 2.8 案例中,是基于 策略模式 进行优化的。这样的话,有一个明显的缺点:策略实现类会非常多!没法俯视整个分派的业务逻辑。 所以,这次的优化使用了 Map + 函数式 接口来减少类的产生。 Java 8 函数式接口:Java 8 中新增了许多函数式接口。这里,我使用的函数式接口是:Consumer:接受一个输入参数并且无返回的操作 接下来看看如何使用它~~ 1、先定义一个消息类型处理器 MsgTypeHandler @Component public class MsgTypeHandler { @Autowired private MsgTypeService msgTypeService; // Consumer:要求只有一个入参,且为 String 类型 private Map msgTypeMap = new HashMap(); @PostConstruct public void dispatcherInit() { msgTypeMap.put("文本", msgType -> msgTypeService.handlerTextMsg(msgType)); msgTypeMap.put("图片", msgType -> msgTypeService.handlerImageMsg(msgType)); } public void handler(String msgType) { Consumer consumer = msgTypeMap.get(msgType); if (null != consumer) { consumer.accept(msgType); return; } System.out.println("没有需要处理的消息类型~~"); } }上述代码用上了 Java 8 的新特性:lambda 表达式 判断条件放在 key 中对应的业务逻辑放在 value 中(是一个方法)这样子写的好处是非常直观,能直接看到判断条件对应的业务逻辑。 2、业务逻辑类 MsgTypeServiceImpl @Service public class MsgTypeServiceImpl implements MsgTypeService { @Override public void handlerTextMsg(String msgType) { System.out.println("处理文本消息"); } @Override public void handlerImageMsg(String msgType) { System.out.println("处理图片消息"); } }3、单元测试 @RunWith(SpringRunner.class) @SpringBootTest public class MsgServiceContextTest { @Autowired private MsgTypeHandler msgTypeHandler; @Test public void contextLoads2() { String msgType = "文本"; msgTypeHandler.handler(msgType); } } |
CopyRight 2018-2019 实验室设备网 版权所有 |